
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include <linux/mod_devicetable.h>
#include <linux/of_device.h>

#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <asm/current.h>
#include <linux/delay.h>


static int major;
static struct class *dht11_class;
static struct gpio_desc *dht11_gpiod;

static int data = 0;
static int irq;
static u64  irq_edge_time_ns[100];
static int irq_cnt = 0;

static DECLARE_WAIT_QUEUE_HEAD(dht11_wait_queue);  			 /*创建并初始化等待队列*/



static int dht11_open(struct inode *node, struct file *file)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}




static int dht11_start(void)
{
	u64 pre_time_ns = 0;
	u64 now_time_ns = 0;
	

	/*设置引脚为输入输出，并拉高电平20ms,同时作为复位信号*/
	gpiod_direction_output(dht11_gpiod, 1);
	mdelay(20);
	
	/*拉低电平20ms*/
	gpiod_set_value(dht11_gpiod, 0);
	mdelay(20);

	/*拉高电平30us*/
	gpiod_set_value(dht11_gpiod, 1);
	pre_time_ns = ktime_get_ns();
	while(1)
	{
		udelay(1);
		now_time_ns = ktime_get_ns();
		if(now_time_ns - pre_time_ns > 30000)
			break;
	}


	/*设置引脚为输入引脚*/
	gpiod_direction_input(dht11_gpiod);	
	return 0;
	
}


static int dht11_data_parse(unsigned char *data)
{
	int i, j, m;
	int offset;
	for (offset = 0; offset <= 2; offset++)
	{
		/*m作为容错使用，中断有可能是80个、81个、82个*/
		m = offset;
		for (i = 0; i < 5; i++)
		{
			data[i] = 0;
			for (j = 0; j < 8; j++)
			{
				data[i] <<= 1;
			 	if (irq_edge_time_ns[m + i*8 +j +1] - irq_edge_time_ns[m + i*8 +j] >= 40000)
			 		data[i] |= 1;
			}
		}
			
		if (data[4] == data[0] + data[1] + data[2] + data[3])
		{
			return 0;
		}
	}
	
	return -1;

}


#if 0

int dht11_parse_data(char *data)
{
	int i, j, m = 0;
	int offset = 0;

	for (offset = 0; offset <= 2; offset ++)
	{
		m = offset;
		
		for (i = 0; i < 5; i++)
		{
			data[i] = 0;
			for (j = 0; j < 8; j++)
			{
				data[i] <<= 1;
				if (dht11_edge_time[m+1] - dht11_edge_time[m] >= 40000)
					data[i] |= 1;
				m += 2;	
			}
		}

		if (data[4] == data[0] + data[1] + data[2] + data[3])
		{
			return 0;
		}
	}

	printk("%s %s line %d, dht11_edge_cnt = %d\n", __FILE__, __FUNCTION__, __LINE__, dht11_edge_cnt);
	return -1;
}
	


#endif


static irqreturn_t dht11_isr(int num, void *dev)
{

	irq_edge_time_ns[irq_cnt++] = ktime_get_boot_ns();

	if (irq_cnt >= 82)
	{
		data = 1;
		wake_up(&dht11_wait_queue);
	}
		
	return IRQ_HANDLED;
}



static ssize_t dht11_read(struct file *file, char __user *buf, size_t size, loff_t *offsize)
{

	int ret;
	int ret_start;
	unsigned int timeout;
	unsigned char dht11_data[5];
	data = 0;
	irq_cnt = 0;
	

	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	
	/*主机发送开始信号*/
	ret_start = dht11_start();
	ret = request_irq(irq, dht11_isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "zmq_dht11_irq", NULL);

	timeout = wait_event_timeout(dht11_wait_queue, data, 3*HZ);
	free_irq(irq, NULL);

	printk("interrupt occur cnt = %d\n", irq_cnt);
	printk("ret_start  = %d\n", ret_start);


	if (timeout == 0)
	{
		printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return -EAGAIN;		
	}

	/*解析数据*/

	ret = dht11_data_parse(dht11_data);

	if (ret)
	{
		printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return -EAGAIN;		
	}

	ret = copy_to_user(buf, dht11_data, 4);
	return 4;

}

static int dht11_close(struct inode *node, struct file *file)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


static struct file_operations dht11_ops = {
	.owner  = THIS_MODULE,
	.open    = dht11_open,
	.release = dht11_close,
	.read    = dht11_read,
};






static struct of_device_id of_dht11_match_table[] = {
	{ .compatible = "zmq,dth11", },
	{ },
};

static int dht11_probe(struct platform_device *pdev)
{

	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	dht11_gpiod = gpiod_get(&pdev->dev, NULL, GPIOD_OUT_HIGH);
	irq = gpiod_to_irq(dht11_gpiod);


	device_create(dht11_class, NULL, MKDEV(major, 0), NULL, "zmq_dht11");
	return 0;
	
}


static int dht11_remove(struct platform_device *dev)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(dht11_class, MKDEV(major, 0));
	gpiod_put(dht11_gpiod);   //释放gpio

	
	return 0;
}


static struct platform_driver dht11_platform_drv = {
	.probe = dht11_probe,
	.remove = dht11_remove,
	.driver = 	{
		.name = "dht11",
		.of_match_table = of_dht11_match_table,
	},

};



static  int  dht11_init(void)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	major = register_chrdev(0, "zmq_dht11_chrdrv", &dht11_ops);
	dht11_class = class_create(THIS_MODULE, "zmq_dht11_class");
	if(IS_ERR(dht11_class))
	{
		unregister_chrdev(major, "zmq_dht11_chrdrv");
		return PTR_ERR(dht11_class);
	}
	platform_driver_register(&dht11_platform_drv);
	
	return 0;
}


static void dht11_exit(void)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);

	platform_driver_unregister(&dht11_platform_drv);
	class_destroy(dht11_class);
	unregister_chrdev(major, "zmq_dht11_chrdev");
}


module_init(dht11_init);
module_exit(dht11_exit);
MODULE_LICENSE("GPL");






